package com.lokiy.compiler

import com.lokiy.annotation.FragmentInfo
import com.lokiy.di.FragmentInfoConst
import com.lokiy.di.FragmentInfoItem
import com.squareup.javapoet.CodeBlock
import com.squareup.javapoet.JavaFile
import com.squareup.javapoet.MethodSpec
import com.squareup.javapoet.TypeSpec
import java.security.MessageDigest
import java.text.SimpleDateFormat
import java.util.*
import javax.annotation.processing.AbstractProcessor
import javax.annotation.processing.ProcessingEnvironment
import javax.annotation.processing.RoundEnvironment
import javax.annotation.processing.SupportedAnnotationTypes
import javax.annotation.processing.SupportedSourceVersion
import javax.lang.model.SourceVersion
import javax.lang.model.element.Element
import javax.lang.model.element.Modifier
import javax.lang.model.element.TypeElement
import javax.tools.Diagnostic

@SupportedSourceVersion(SourceVersion.RELEASE_8)
@SupportedAnnotationTypes("com.lokiy.annotation.FragmentInfo")
class FragmentInfoProcessor : AbstractProcessor() {

    override fun getSupportedOptions(): Set<String> {
        return setOf("org.gradle.annotation.processing.aggregating")
    }

    override fun process(annotations: MutableSet<out TypeElement>?, roundEnvironment: RoundEnvironment?): Boolean {
        println("----------------------- process")
        annotations ?: return false
        roundEnvironment ?: return false
        val simpleDateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.CHINA)

//        processingEnv.messager.printMessage(Diagnostic.Kind.NOTE, annotations.joinToString { it.simpleName.toString() })
        annotations.forEach { annotation ->
            if (annotation.simpleName.endsWith(FragmentInfo::class.java.simpleName)) {
                val elementsAnnotatedWith = roundEnvironment.getElementsAnnotatedWith(annotation)
                runCatching {
                    if (elementsAnnotatedWith.isNotEmpty()) {
                        val init = MethodSpec.methodBuilder(FragmentInfoConst.METHOD)
                            .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                            .returns(Void.TYPE)
                            .addStatement("try {")
                            .addStatement(CodeBlock.join(elementsAnnotatedWith.sortedBy { it.simpleName.toString() }
                                .map {
                                    CodeBlock.of("\$T.addFragment(\$S)", FragmentInfoItem::class.java, it.toString())
                                }, ";\n"))
                            .addStatement("} catch(Exception e) {}")
                            .build()
                        val className = FragmentInfoConst.NAME
                        val javaDoc = mutableListOf(className, "generated at ${simpleDateFormat.format(Date())}", "@author Lokiy")
                        val infoClass = TypeSpec.classBuilder(className)
                            .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
                            .addJavadoc(CodeBlock.join(javaDoc.map { CodeBlock.of(it) }, "\n"))
                            .addMethod(init).build()
                        val javaFile = JavaFile.builder(FragmentInfoConst.PKG, infoClass).build()
                        try {
                            javaFile.writeTo(processingEnv.filer)
                        } catch (e: Exception) {
                            e.printStackTrace()
                        }
                    }
                }.onFailure {
                    println("----------------onFailure -> $it ")
                }
            }
        }

        return false
    }

    override fun getSupportedAnnotationTypes(): MutableSet<String> {
        return mutableSetOf(FragmentInfo::class.java.name)
    }
}